package com.cosmoplat.xikai.business.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cosmoplat.xikai.business.domain.AgvCallbackLog;
import com.cosmoplat.xikai.business.domain.DeviceLog;
import com.cosmoplat.xikai.business.domain.DeviceUsageRecord;
import com.cosmoplat.xikai.business.domain.back.EquipmentUseBackup;
import com.cosmoplat.xikai.business.enums.DeviceParamsEnum;
import com.cosmoplat.xikai.business.enums.DeviceRunStatusEnum;
import com.cosmoplat.xikai.business.mapper.EquipmentUseBackupMapper;
import com.cosmoplat.xikai.business.scada.ScadaData;
import com.cosmoplat.xikai.business.service.AgvService;
import com.cosmoplat.xikai.business.service.DeviceUsageRecordService;
import com.cosmoplat.xikai.business.service.JobService;
import com.cosmoplat.xikai.common.utils.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;

/**
 * @ClassName JobServiceImpl
 * @Description 定时任务-实现类
 * @Author wdf
 * @Date 2025/9/5
 */
@Service
@Slf4j
public class JobServiceImpl implements JobService {

    @Autowired
    private ScadaData scadaData;

    @Autowired
    private DeviceUsageRecordService deviceUsageRecordService;

    @Autowired
    private EquipmentUseBackupMapper equipmentUseBackupMapper;

    @Autowired
    private AgvService agvService;

    @Value("${device.code}")
    private String deviceCode;

    @Override
    @Transactional
    public void deviceStatusStatistics(String deviceCode1) {
        log.debug("设备状态信息更新...");
        List<DeviceUsageRecord> deviceUsageRecords = new ArrayList<>();
        LocalDateTime now = LocalDateTime.now();

        // 获取当前时间戳（精确到分钟）
        Date currentMinute = convertLocalDateTimeToDate(now.withSecond(0).withNano(0));
        Date workingDate = convertLocalDateToDate(now.toLocalDate());

        try {
            // 获取设备当前运行状态
            DeviceLog runStatusLog = scadaData.getDeviceLog(DeviceParamsEnum.RUN_STATE.getName());

            if (runStatusLog != null) {
                // 处理设备状态记录
                processDeviceStatus(deviceCode, runStatusLog, currentMinute, workingDate, deviceUsageRecords);
            } else {
                log.warn("设备 {} 未获取到运行状态", deviceCode);
                // 如果未获取到状态，记录为离线状态  - 不记录
                //processDeviceStatus(deviceCode, null, currentMinute, workingDate, deviceUsageRecords);
            }
        } catch (Exception e) {
            log.error("处理设备 {} 状态时发生异常", deviceCode, e);
            // 异常情况下记录为离线状态 - 不记录
            //processDeviceStatus(deviceCode, null, currentMinute, workingDate, deviceUsageRecords);
        }

        // 更新设备使用备份表
        updateEquipmentUseBackup(deviceUsageRecords);
    }

    @Override
    public void agvCheck() {
        // 等待中进入的任务
       List<AgvCallbackLog> waitingTaskList = agvService.waitingTaskList();
       if (CollectionUtils.isEmpty(waitingTaskList)) {
           log.debug("agv无等待中任务");
           return;
       }
       // 检查设备是否允许进入
       agvService.checkDeviceForContinueTask(waitingTaskList);
    }

    /**
     * 处理设备状态记录
     * 根据当前状态记录1分钟的使用时长
     * 所有状态（包括离线状态）都会累计时长
     */
    private void processDeviceStatus(String deviceCode, DeviceLog runStatusLog,
                                     Date currentMinute, Date workingDate,
                                     List<DeviceUsageRecord> records) {
        // 获取设备状态，如果为空或获取失败则设置为离线状态
        String deviceStatus = (runStatusLog != null) ? runStatusLog.getVal() : null;
        if (deviceStatus == null || deviceStatus.equals(DeviceRunStatusEnum.OFFLINE.getCode())) {
            log.warn("离线或者未获取到运行状态，不记录运行信息:{}", runStatusLog);
            return;
            // deviceStatus = DeviceRunStatusEnum.OFFLINE.getCode();
        }

        // 查找是否已有该设备该状态该分钟的记录
        List<DeviceUsageRecord> existingRecordList = deviceUsageRecordService.list(
                new LambdaQueryWrapper<DeviceUsageRecord>()
                        .eq(DeviceUsageRecord::getCode, deviceCode)
                        // 每天只有1条数据，1分钟更新一次
                        // SQL Server
                        .apply("FORMAT(data_time, 'yyyy-MM-dd') = {0}",
                                new SimpleDateFormat("yyyy-MM-dd").format(currentMinute))
        );

        DeviceUsageRecord record;
        if (CollectionUtils.isNotEmpty(existingRecordList)) {
            // 更新现有记录：增加1分钟时长
            record = existingRecordList.get(0);
            // 工作时间
            if (DeviceRunStatusEnum.workingStatus(deviceStatus)){
                record.setWorkingHours(record.getWorkingHours() + 1); // 总时长增加1分钟
            }


            // 对于所有状态（包括离线状态）都累计时长
            // 开机时间：只有非离线状态才累计
            if (!deviceStatus.equals(DeviceRunStatusEnum.OFFLINE.getCode())) {
                record.setBootTime(record.getBootTime() + 1);
            }
        } else {
            // 创建新记录：记录1分钟时长
            record = new DeviceUsageRecord();
            record.setCode(deviceCode);
            record.setDeviceStatus(deviceStatus);
            record.setWorkingDate(workingDate);
            // 工作时间
            if (DeviceRunStatusEnum.workingStatus(deviceStatus)){
                record.setWorkingHours(1L); // 当前状态持续1分钟
            }
            record.setDataTime(currentMinute);

            // 开机时间：非离线状态记录1分钟，离线状态记录0
            record.setBootTime(deviceStatus.equals(DeviceRunStatusEnum.OFFLINE.getCode()) ? 0L : 1L);
        }

        // 保存或更新记录
        deviceUsageRecordService.saveOrUpdate(record);
        records.add(record);
    }

    /**
     * 更新设备使用备份表（按天汇总）
     * 包含离线状态的累计计算
     */
    private void updateEquipmentUseBackup(List<DeviceUsageRecord> usageRecordList) {
        if (usageRecordList.isEmpty()) {
            return;
        }
        DeviceUsageRecord usageRecord = usageRecordList.get(0);
        // 保存或更新备份记录
        EquipmentUseBackup existing = equipmentUseBackupMapper.selectOne(
                new LambdaQueryWrapper<EquipmentUseBackup>()
                        .eq(EquipmentUseBackup::getDevice, usageRecord.getCode())
                        .apply("FORMAT(WorkDate, 'yyyy-MM-dd') = {0}",
                                new SimpleDateFormat("yyyy-MM-dd").format(usageRecord.getWorkingDate()))
        );
        EquipmentUseBackup backup;
        if (existing != null) {
            backup = existing;
            // 更新现有记录：累加时长
            backup.setPrimaryKey(existing.getPrimaryKey());
            backup.setOnhours(usageRecord.getBootTime());
            backup.setWorkhours(usageRecord.getWorkingHours());
            equipmentUseBackupMapper.updateById(backup);
        } else {
            backup = new EquipmentUseBackup();
            backup.setDevice(deviceCode);
            backup.setPrimaryKey(usageRecord.getId());
            backup.setWorkDate(DateUtils.dateTime(usageRecord.getWorkingDate()));
            backup.setOnhours(usageRecord.getBootTime());
            backup.setWorkhours(usageRecord.getWorkingHours());
            equipmentUseBackupMapper.insert(backup);
        }
    }

    /**
     * 判断是否为工作状态（生产相关状态）
     */
    private boolean isWorkingStatus(String status) {
        return DeviceRunStatusEnum.START_PRODUCTION.getCode().equals(status) ||
                DeviceRunStatusEnum.REPRODUCTION.getCode().equals(status) ||
                DeviceRunStatusEnum.COMPLETION_STATUS_ACQUISITION.getCode().equals(status);
    }

    /**
     * 日期转换辅助方法
     */
    private Date convertLocalDateTimeToDate(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    private Date convertLocalDateToDate(LocalDate localDate) {
        return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
    }

    private String formatDate(Date date) {
        return new SimpleDateFormat("yyyy-MM-dd").format(date);
    }
}